/*@internal*/
名域 ts {
    常量 系统格式化诊断主机: 格式化诊断主机_ = sys ? {
        获取当前目录: () => sys.获取当前目录(),
        获取新行: () => sys.新行,
        获取规范文件名: 创建获取规范文件名称(sys.使用区分大小写文件名)
    } : 未定!;

    /**
     * Create a function that reports error by writing to the system and handles the formating of the 诊断
     */
    导出 函数 创建诊断报告(系统: 系统_, 漂亮的?: 真假): 诊断报告器_ {
        常量 主机: 格式化诊断主机_ = 系统 === sys ? 系统格式化诊断主机 : {
            获取当前目录: () => 系统.获取当前目录(),
            获取新行: () => 系统.新行,
            获取规范文件名: 创建获取规范文件名称(系统.使用区分大小写文件名),
        };
        如果 (!漂亮的) {
            返回 诊断 => 系统.写出(格式化诊断(诊断, 主机));
        }

        常量 诊断组: 诊断_[] = 新建 数组_(1);
        返回 诊断 => {
            诊断组[0] = 诊断;
            系统.写出(格式化诊断包括颜色和上下文(诊断组, 主机) + 主机.获取新行());
            诊断组[0] = 未定!;
        };
    }

    函数 清除屏幕如果不关注文件更改(系统: 系统_, 诊断: 诊断_, 选项组: 编译选项_) {
        如果 (系统.清除屏幕 &&
            !选项组.保留监控输出 &&
            诊断.代码 !== 诊断信息_.编译完成_监视文件更改.代码 &&
            !选项组.扩展的诊断组 &&
            !选项组.诊断组) {
            系统.清除屏幕();
        }
    }

    /**
     * Create a function that reports watch status by writing to the system and handles the formating of the 诊断
     */
    导出 函数 创建监控状态报告(系统: 系统_, 漂亮的?: 真假): 监控状态报告_ {
        返回 漂亮的 ?
            (诊断, 新行, 选项组) => {
                清除屏幕如果不关注文件更改(系统, 诊断, 选项组);
                变量 输出 = `[${格式化颜色和剩余(新建 日期_().转为本地时间文字(), 前景颜色转义序列_.Grey)}] `;
                输出 += `${拼合诊断信息文本(诊断.消息文本, 系统.新行)}${新行 + 新行 + 新行}`;
                系统.写出(输出);
            } :
            (诊断, 新行, 选项组) => {
                清除屏幕如果不关注文件更改(系统, 诊断, 选项组);
                变量 输出 = 新建 日期_().转为本地时间文字() + ' - ';
                输出 += `${拼合诊断信息文本(诊断.消息文本, 系统.新行)}${新行 + 新行 + 新行}`;
                系统.写出(输出);
            };
    }

    /**
     * Interface extending ParseConfigHost to support ParseConfigFile that reads config 文件 and reports errors
     */
    导出 接口 解析配置文件主机_ 扩展 解析配置主机_, 配置文件诊断信息报告_ {
        创建目录(目录路径: 文字): 无值;
        目录存在(目录路径: 文字): 真假;
        获取当前目录(): 文字;
        获取目录组(路径: 文字): 文字[];
    }

    /** Parses config 文件 using 系统_ interface */
    导出 函数 解析配置文件包括系统(配置文件名称: 文字, 系统: 系统_, 报告诊断信息: 诊断报告器_) {
        常量 主机: 解析配置文件主机_ = <任意>系统;
        主机.正在配置文件诊断 = 报告诊断信息;
        主机.正在不可恢复的配置文件诊断信息 = 诊断 => 报告不可恢复诊断(sys, 报告诊断信息, 诊断);
        常量 结果 = 获取配置文件的解析命令行(配置文件名称, 主机);
        主机.正在配置文件诊断 = 未定!;
        主机.正在不可恢复的配置文件诊断信息 = 未定!;
        返回 结果;
    }

    /**
     * Reads the config 文件, reports errors if any and exits if the config 文件 cannot be found
     */
    导出 函数 获取配置文件的解析命令行(配置文件名称: 文字, 主机: 解析配置文件主机_): 解析命令行_ | 未定 {
        变量 配置文件文本: 文字;
        尝试 {
            配置文件文本 = 主机.读文件(配置文件名称)!;
        }
        捕获 (e) {
            常量 错误 = 创建编译器诊断(诊断信息_.无法读取文件_0_Colon_1, 配置文件名称, e.消息);
            主机.正在不可恢复的配置文件诊断信息(错误);
            返回 未定;
        }
        如果 (!配置文件文本) {
            常量 错误 = 创建编译器诊断(诊断信息_.没发现文件_0, 配置文件名称);
            主机.正在不可恢复的配置文件诊断信息(错误);
            返回 未定;
        }
        尝试 {
            常量 配置文件内容 = JSON.解析(配置文件文本) 转为 编译选项_;
            常量 结果: 解析命令行_ = {
                选项组: {
                    系统: 配置文件内容.系统,
                    架构: 配置文件内容.架构,
                    定义: 配置文件内容.定义,
                    字符: 配置文件内容.字符,
                    监控: 配置文件内容.监控,
                    输出: 配置文件内容.输出,
                    启用诊断: 配置文件内容.启用诊断,
                    扩展的诊断组: 配置文件内容.扩展的诊断组,
                    保留监控输出: 配置文件内容.保留监控输出,
                    彩色: 为真,
                },
                保存时编译: 为真,
            }
            返回 结果;
        }
        捕获 {
            常量 错误 = 创建编译器诊断(诊断信息_.没发现文件_0, 配置文件名称);
            主机.正在不可恢复的配置文件诊断信息(错误);
            返回 未定
        }
    }

    /**
     * Program structure needed to emit the files and report 诊断组
     */
    导出 接口 程序到输出文件和报告错误_ {
        获取当前目录(): 文字;
        获取编译选项(): 编译选项_;
        获取源文件组(): 只读数组_<源文件_>;
        获取语法诊断信息(): 只读数组_<诊断_>;
        获取选项诊断信息(): 只读数组_<诊断_>;
        获取全局诊断信息(): 只读数组_<诊断_>;
        获取语义诊断信息(): 只读数组_<诊断_>;
        输出(): 输出结果_;
    }

    /**
     * Helper that emit files, report 诊断组 and lists emitted and/or source files depending on compiler options
     */
    导出 函数 输出文件和报告错误(程序: 程序到输出文件和报告错误_, 报告诊断: 诊断报告器_, 写文件名称?: (s: 文字) => 无值) {
        // First get and report any syntactic errors.
        常量 诊断 = 程序.获取语法诊断信息().分切();
        变量 报告语言诊断 = 为假;

        // If we didn't have any syntactic errors, then also try getting the global and
        // semantic errors.
        如果 (诊断.长度 === 0) {
            添加范围(诊断, 程序.获取选项诊断信息());
            // 添加范围(诊断, 程序.获取全局诊断信息());

            如果 (诊断.长度 === 0) {
                报告语言诊断 = 为真;
            }
        }

        // Emit and report any errors we ran into.
        常量 { 输出文件, 输出跳过, 诊断组 } = { 输出文件: [], 输出跳过: 为真, 诊断组: [] }// 程序.输出();
        添加范围(诊断, 诊断组);

        如果 (报告语言诊断) {
            添加范围(诊断, 程序.获取语义诊断信息());
        }

        排序和消除重复诊断信息(诊断).循环执行(报告诊断);
        如果 (写文件名称) {
            常量 当前目录 = 程序.获取当前目录();
            循环执行(输出文件, 文件 => {
                常量 文件路径 = 获取标准化绝对路径(文件, 当前目录);
                写文件名称(`KLANGFILE: ${文件路径}`);
            });

            如果(程序.获取编译选项().打印文件列表) {
                循环执行(程序.获取源文件组(), 文件 => {
                    写文件名称(文件.文件名);
                });
            }
        }

        如果 (输出跳过 && 诊断组.长度 > 0) {
            // If the emitter didn't emit anything, then pass that value along.
            返回 退出状态_.诊断当前_已跳过输出_;
        }
        否则 如果 (诊断组.长度 > 0) {
            // The emitter emitted something, inform the caller if that happened in the presence
            // of 诊断组 or not.
            返回 退出状态_.诊断当前_输出生成_;
        }
        返回 退出状态_.成功_;
    }

    常量 什么都不做文件监控者: 文件监控_ = { close: 什么都不做 };

    /**
     * Creates the watch compiler host that can be extended with config 文件 or root 文件 names and options host
     */
    函数 创建监控编译主机<T 扩展 构建程序_ = 输出和可视化语义诊断构建程序_>(系统 = sys, 创建程序?: 创建程序_<T>, 报告诊断?: 诊断报告器_, 报告监控状态?: 监控状态报告_): 监控编译主机_<T> {
        如果 (!创建程序) {
            创建程序 = 创建输出和语义诊断生成器程序 转为 任意;
        }

        常量 使用区分大小写文件名 = () => 系统.使用区分大小写文件名;
        常量 写文件名称 = (s: 文字) => 系统.写出(s + 系统.新行);
        返回 {
            使用区分大小写文件名: 使用区分大小写文件名,
            获取新行: () => 系统.新行,
            获取当前目录: () => 系统.获取当前目录(),
            获取默认支持库位置: 获取默认支持库位置,
            获取默认支持库文件名: () => 合并路径(获取默认支持库位置(), 获取默认支持库文件名()),
            文件存在: 路径 => 系统.文件存在(路径),
            读文件: (路径, 编码) => 系统.读文件(路径, 编码),
            目录存在: 路径 => 系统.目录存在(路径),
            获取目录组: 路径 => 系统.获取目录组(路径),
            读目录: (路径, 扩展名组, 深度) => 系统.读目录(路径, 扩展名组, 深度),
            真实路径: 系统.真实路径 && (路径 => 系统.真实路径!(路径)),
            获取环境变量: 系统.获取环境变量 && (名称 => 系统.获取环境变量(名称)),
            监控文件: 系统.监控文件 ? ((路径, 回调, 轮询间隔) => 系统.监控文件!(路径, 回调, 轮询间隔)) : () => 什么都不做文件监控者,
            监控目录: 系统.监控目录 ? ((路径, 回调, 递归) => 系统.监控目录!(路径, 回调, 递归)) : () => 什么都不做文件监控者,
            设置超时: 系统.设置超时 ? ((回调, 消息, ...实参: 任意[]) => 系统.设置超时!.调用(系统, 回调, 消息, ...实参)) : 什么都不做,
            清除超时: 系统.清除超时 ? (超时Id => 系统.清除超时!(超时Id)) : 什么都不做,
            跟踪: s => 系统.写出(s),
            正在监控状态改变: 报告监控状态 || 创建监控状态报告(系统),
            创建目录: 路径 => 系统.创建目录(路径),
            写文件: (路径, 数据, 写字节或标记) => 系统.写文件(路径, 数据, 写字节或标记),
            正在缓存目录结构主机创建: 缓存主机 =>  缓存主机 || 系统,
            创建哈希: 系统.创建哈希 && (s => 系统.创建哈希!(s)),
            创建程序: 创建程序!,
            程序创建之后: 输出文件和报告错误使用构建器
        }

        函数 获取默认支持库位置() {
            返回 获取目录路径(正规化路径(合并路径(系统.获取当前目录(), 'test/模块/lib/')))  // 获取目录路径(正规化路径(sys.获取正执行的文件路径()));
        }

        函数 输出文件和报告错误使用构建器(构建程序: 构建程序_) {
            输出文件和报告错误(构建程序, 报告诊断!, 写文件名称);
        }
    }

    /**
     * Report error and exit
     */
    函数 报告不可恢复诊断(系统: 系统_, 报告诊断: 诊断报告器_, 诊断: 诊断_) {
        报告诊断(诊断);
        系统.退出(退出状态_.诊断当前_已跳过输出_);
    }

    /**
     * Creates the watch compiler host from system for config 文件 in watch mode
     */
    导出 函数 创建配置文件的监控编译主机<T 扩展 构建程序_ = 输出和可视化语义诊断构建程序_>(配置文件名称: 文字, 系统: 系统_, 创建程序?: 创建程序_<T>, 报告诊断信息?: 诊断报告器_, 报告监控状态?: 监控状态报告_): 监控配置文件的编译主机_<T> {
        报告诊断信息 = 报告诊断信息 || 创建诊断报告(系统);
        常量 主机 = 创建监控编译主机(系统, 创建程序, 报告诊断信息, 报告监控状态) 转为 监控配置文件的编译主机_<T>;
        主机.正在配置文件诊断 = 报告诊断信息;
        主机.正在不可恢复的配置文件诊断信息 = 诊断 => 报告不可恢复诊断(系统, 报告诊断信息!, 诊断);
        主机.配置文件名称 = 配置文件名称;
        返回 主机;
    }

    /**
     * Creates the watch compiler host from system for compiling root files and options in watch mode
     */
    导出 函数 创建文件和编译选项的监控编译主机<T 扩展 构建程序_ = 输出和可视化语义诊断构建程序_>(根文件组: 文字[], 选项组: 编译选项_, 系统: 系统_, 创建程序?: 创建程序_<T>, 报告诊断?: 诊断报告器_, 报告监控状态?: 监控状态报告_): 监控文件和编译选项的编译主机<T> {
        常量 主机 = 创建监控编译主机(系统, 创建程序, 报告诊断 || 创建诊断报告(系统), 报告监控状态) 转为 监控文件和编译选项的编译主机<T>;
        主机.根文件组 = 根文件组;
        主机.选项组 = 选项组;
        返回 主机;
    }
}

名域 ts {

    导出 类型 诊断报告器_ = (诊断: 诊断_) => 无值;

    导出 类型 监控状态报告_ = (诊断: 诊断_, 新行: 文字, 选项: 编译选项_) => 无值;

    导出 类型 创建程序_<T 扩展 构建程序_> = (根名称组: 只读数组_<文字>, 选项: 编译选项_, 主机?: 编译主机_, 旧程序?: T) => T;

    导出 接口 监控编译主机_<T 扩展 构建程序_> {
        /**
         * Used to create the program when need for program creation or recreation detected
         */
        创建程序: 创建程序_<T>;
        /** If provided, callback to invoke after every new program creation */
        程序创建之后?(程序: T): 无值;
        /** If provided, called with 诊断 消息 that informs about change in watch status */
        正在监控状态改变?(诊断: 诊断_, 新行: 文字, 选项组: 编译选项_): 无值;

        // Only for testing
        /*@internal*/
        最大文件迭代失败数量?: 数字;

        // Sub set of compiler host methods to read and generate new program
        使用区分大小写文件名(): 真假;
        获取新行(): 文字;
        获取当前目录(): 文字;
        获取默认支持库文件名(): 文字;
        获取默认支持库位置?(): 文字;
        创建哈希?(数据: 文字): 文字;

        /**
         * Use to check 文件 presence for source files and
         * if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well
         */
        文件存在(路径: 文字): 真假;
        /**
         * Use to read 文件 文本 for source files and
         * if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well
         */
        读文件(路径: 文字, 编码?: 文字): 文字 | 未定;

        /** If provided, used for module resolution as well as to handle directory structure */
        目录存在?(路径: 文字): 真假;
        /** If provided, used in resolutions as well as handling directory structure */
        获取目录组?(路径: 文字): 文字[];
        /** If provided, used to cache and handle directory structure modifications */
        读目录?(路径: 文字, 扩展名组?: 只读数组_<文字>, 深度?: 数字): 文字[];

        /** 符号_ links resolution */
        真实路径?(路径: 文字): 文字;
        /** If provided would be used to write log about compilation */
        跟踪?(s: 文字): 无值;
        /** If provided is used to get the environment variable */
        获取环境变量?(名称: 文字): 文字;

        /** If provided, used to resolve the module names, otherwise typescript's 默认 module resolution */
        解析模块名称组?(模块名称: 文字[], 包含文件: 文字, 重复使用的名称?: 文字[]): 模块_[];

        /** Used to watch changes in source files, 失踪的 files needed to update the program or config 文件 */
        监控文件(路径: 文字, 回调: 文件监控回调_, 轮询间隔?: 数字): 文件监控_;
        /** Used to watch resolved module's failed lookup locations, config 文件 specs, 类型 roots where auto 类型 reference directives are added */
        监控目录(路径: 文字, 回调: 目录监控回调_, 递归?: 真假): 文件监控_;
        /** If provided, will be used to set delayed compilation, so that multiple changes in short 跨度 are compiled together */
        设置超时?(回调: (...实参: 任意[]) => 无值, 消息: 数字, ...实参: 任意[]): 任意;
        /** If provided, will be used to reset existing delayed compilation */
        清除超时?(超时id: 任意): 无值;
    }

    /** Internal interface used to wire emit through same host */
    /*@internal*/
    导出 接口 监控编译主机_<T 扩展 构建程序_> {
        创建目录?(路径: 文字): 无值;
        写文件?(路径: 文字, 数据: 文字, 写入字节序标记?: 真假): 无值;
        正在缓存目录结构主机创建?(主机: 缓存目录文件主机_): 无值;
    }

    /**
     * Host to create watch with root files and options
     */
    导出 接口 监控文件和编译选项的编译主机<T 扩展 构建程序_> 扩展 监控编译主机_<T> {
        /** root files to use to generate program */
        根文件组: 文字[];

        /** Compiler options */
        选项组: 编译选项_;
    }

    /**
     * Reports config 文件 诊断组
     */
    导出 接口 配置文件诊断信息报告_ {
        /**
         * Reports the 诊断组 in reading/writing or parsing of the config 文件
         */
        正在配置文件诊断: 诊断报告器_;

        /**
         * Reports unrecoverable error when parsing config 文件
         */
        正在不可恢复的配置文件诊断信息: 诊断报告器_;
    }

    /**
     * Host to create watch with config 文件
     */
    导出 接口 监控配置文件的编译主机_<T 扩展 构建程序_> 扩展 监控编译主机_<T>, 配置文件诊断信息报告_ {
        /** Name of the config 文件 to compile */
        配置文件名称: 文字;
        /**
         * Used to generate source 文件 names from the config 文件 and its include, exclude, files rules
         * and also to cache the directory stucture
         */
        读目录(路径: 文字, 扩展名组?: 只读数组_<文字>, 深度?: 数字): 文字[];
    }

    /**
     * Host to create watch with config 文件 that is already parsed (from tsc)
     */
    /*@internal*/
    导出 接口 监控配置文件的编译主机_<T 扩展 构建程序_> 扩展 监控编译主机_<T> {
        根文件组?: 文字[];
        选项组?: 编译选项_;
    }

    导出 接口 监控_<T> {
        /** Synchronize with host and get updated program */
        获取程序(): T;
        /** Gets the existing program without synchronizing with changes on host */
        /*@internal*/
        获取当前程序(): T;
    }

    /**
     * Creates the watch what generates program using the config 文件
     */
    导出 接口 配置文件的监控_<T> 扩展 监控_<T> {
    }

    /**
     * Creates the watch that generates program using the root files and compiler options
     */
    导出 接口 文件和编译选项的监控_<T> 扩展 监控_<T> {
        /** Updates the root files in the program, only if this is not config 文件 compilation */
        更新根文件名称(文件名组: 文字[]): 无值;
    }

    /**
     * Create the watch compiler host for either configFile or fileNames and its options
     */
    导出 函数 创建监控编译主机<T 扩展 构建程序_>(根文件: 文字[], 选项: 编译选项_, 系统: 系统_, 创建程序?: 创建程序_<T>, 报告诊断?: 诊断报告器_, 报告监控状态?: 监控状态报告_): 监控文件和编译选项的编译主机<T>;
    导出 函数 创建监控编译主机<T 扩展 构建程序_>(配置文件名称: 文字, 选项到扩展: 编译选项_ | 未定, 系统: 系统_, 创建程序?: 创建程序_<T>, 报告诊断?: 诊断报告器_, 报告监控状态?: 监控状态报告_): 监控配置文件的编译主机_<T>;
    导出 函数 创建监控编译主机<T 扩展 构建程序_>(根文件或配置文件名称: 文字 | 文字[], 选项: 编译选项_ | 未定, 系统: 系统_, 创建程序?: 创建程序_<T>, 报告诊断?: 诊断报告器_, 报告监控状态?: 监控状态报告_): 监控文件和编译选项的编译主机<T> | 监控配置文件的编译主机_<T> {
        如果 (是数组(根文件或配置文件名称)) {
            返回 创建文件和编译选项的监控编译主机(根文件或配置文件名称, 选项!, 系统, 创建程序, 报告诊断, 报告监控状态);
        }
        否则 {
            返回 创建配置文件的监控编译主机(根文件或配置文件名称, 系统, 创建程序, 报告诊断, 报告监控状态);
        }
    }

    常量 初始化版本 = 1;

    /**
     * Creates the watch from the host for root files and compiler options
     */
    导出 函数 创建监控程序<T 扩展 构建程序_>(主机: 监控文件和编译选项的编译主机<T>): 文件和编译选项的监控_<T>;
    /**
     * Creates the watch from the host for config 文件
     */
    导出 函数 创建监控程序<T 扩展 构建程序_>(主机: 监控配置文件的编译主机_<T>): 配置文件的监控_<T>;
    导出 函数 创建监控程序<T 扩展 构建程序_>(主机: 监控文件和编译选项的编译主机<T> & 监控配置文件的编译主机_<T>): 文件和编译选项的监控_<T> | 配置文件的监控_<T> {
        接口 主机存在的文件_ {
            版本: 数字;
            源文件: 源文件_;
            文件监控者: 文件监控_;
        }
        类型 主机失踪的文件_ = 数字;
        接口 主机存在未知的文件_ {
            版本: 数字;
        }
        类型 文件可能在主机上_ = 主机存在的文件_ | 主机存在未知的文件_;
        类型 主机文件信息_ = 主机存在的文件_ | 主机失踪的文件_ | 主机存在未知的文件_;

        变量 构建程序: T;
        变量 重加载级别: 配置文件程序加载级别_;                      // level to indicate if the program needs to be reloaded from config 文件/just filenames etc
        变量 失踪文件词典: 词典_<文件监控_>;                              // 词典_ of 文件 watchers for the 失踪的 files
        变量 监控通配符目录: 词典_<通配符目录监控者_>;      // map of watchers for the wild card directories in the config 文件
        变量 更新程序定时器: 任意;                                      // timer callback to recompile the program

        常量 源文件缓存 = 创建词典<主机文件信息_>();                 // Cache that stores the source 文件 and version info
        变量 失踪请求文件路径解析: 路径_[];                    // These paths are held temparirly so that we can remove the entry from source 文件 cache if the 文件 is not tracked by 失踪的 files
        变量 具有改变编译选项 = 为假;                              // 真_ if the compiler options have changed between compilations
        变量 具有改变自动类型指令名称 = 为假;                  // 真_ if the automatic 类型 directives have changed

        常量 使用区分大小写文件名 = 主机.使用区分大小写文件名();
        常量 当前目录 = 主机.获取当前目录();
        常量 获取当前目录 = () => 当前目录;
        常量 读文件: (路径: 文字, 编码?: 文字) => 文字 | 未定 = (路径, 编码) => 主机.读文件(路径, 编码);
        常量 { 配置文件名称, 创建程序 } = 主机;
        变量 { 根文件组, 选项组 } = 主机;

        常量 缓存目录结构主机 = 配置文件名称 ? 创建缓存目录结构主机(主机, 当前目录, 使用区分大小写文件名) : 未定;
        如果 (缓存目录结构主机 && 主机.正在缓存目录结构主机创建) {
            主机.正在缓存目录结构主机创建(缓存目录结构主机);
        }
        常量 目录结构主机: 目录结构主机_ = 缓存目录结构主机 || 主机;
        常量 解析配置文件主机: 解析配置文件主机_ = {
            使用区分大小写文件名: 使用区分大小写文件名,
            创建目录: 路径 => 主机.创建目录!(路径),
            目录存在: 路径 => 主机.目录存在!(路径),
            读目录: (路径, 扩展名组, 深度) => 目录结构主机.读目录!(路径, 扩展名组, 深度),
            文件存在: 路径 => 主机.文件存在(路径),
            获取目录组: 路径 => 主机.获取目录组!(路径),
            读文件,
            获取当前目录,
            正在配置文件诊断: 主机.正在配置文件诊断,
            正在不可恢复的配置文件诊断信息: 主机.正在不可恢复的配置文件诊断信息
        };

        // From tsc we want to get already parsed 结果 and hence check for rootFileNames
        如果 (配置文件名称 && !根文件组) {
            解析配置文件();
        }

        常量 跟踪 = 主机.跟踪 && ((s: 文字) => { 主机.跟踪!(s + 新行); });
        常量 监控日志级别 = 跟踪 ? 选项组!.启用诊断 ? 监控日志级别_.详细_ :
            选项组!.诊断 ? 监控日志级别_.仅触发器_ : 监控日志级别_.无_ : 监控日志级别_.无_;
        常量 写日志: (s: 文字) => 无值 = 监控日志级别 !== 监控日志级别_.无_ ? 跟踪! : 什么都不做;
        常量 { 监控文件, 监控文件路径: 监控文件路径 } = 获取监控工厂(监控日志级别, 写日志);

        常量 获取规范文件名称 = 创建获取规范文件名称(使用区分大小写文件名);
        变量 新行 = 更新新行();

        写日志(`当前目录: ${当前目录} 使用区分大小写文件名: ${使用区分大小写文件名}`);
        如果 (配置文件名称) {
            监控文件(主机, 配置文件名称, 计划程序重新加载, 轮询间隔_.High);
        }
        常量 全局模块目录 = 正规化路径(合并路径(当前目录, 'test/模块'));
        常量 当前模块目录 = 正规化路径(合并路径(当前目录, 'test/模块'));
        常量 当前源码目录 = 正规化路径(合并路径(当前目录, 'test/源码'));        
        常量 当前项目配置 = 正规化路径(合并路径(当前目录, 'test/klang.mod.json'));

        常量 编译主机: 编译主机_ & 解析缓存主机_ = {
            // Members for 编译主机_
            获取源文件: (文件名, 正在错误?, 应该创建新源文件?) => 获取按路径版本控制的源文件(文件名, 转为路径(文件名), 正在错误, 应该创建新源文件),
            获取源文件从路径: 获取按路径版本控制的源文件,
            获取默认支持库位置: 主机.获取默认支持库位置 && (() => 主机.获取默认支持库位置!()),
            获取默认支持库文件名: () => 主机.获取默认支持库文件名(),
            写文件,
            获取当前目录: 获取当前目录,
            读目录: 目录结构主机.读目录!,
            使用区分大小写文件名: () => 使用区分大小写文件名,
            获取规范文件名称: 获取规范文件名称,
            获取新行: () => 新行,
            文件存在: 文件存在,
            读文件: 读文件,
            跟踪: 跟踪,
            目录存在: 目录结构主机.目录存在 && (路径 => 目录结构主机.目录存在!(路径)),
            获取目录组: (路径: 文字) => 目录结构主机.获取目录!(路径),
            真实路径: 主机.真实路径 && (s => 主机.真实路径!(s)),
            获取环境变量: 主机.获取环境变量 ? (名称 => 主机.获取环境变量!(名称)) : (() => ''),
            正在发布旧源文件: 正在发布旧源文件,
            创建哈希: 主机.创建哈希 && (数据 => 主机.创建哈希!(数据)),
            // Members for ResolutionCacheHost
            转为路径: 转为路径,
            获取编译设置: () => 选项组!,
            获取缓存目录结构主机: () => 缓存目录结构主机,
            正在无效决议: 计划程序更新,
            最大文件迭代失败数量: 主机.最大文件迭代失败数量,
            获取当前程序,
            写日志: 写日志,
            全局模块目录,
            当前模块目录,
            当前源码目录,
            当前项目配置
        };

        
        常量 依赖 = 读Json(正规化路径(合并路径(当前目录, 'test/klang.mod.json')), 编译主机).依赖 || {}
        常量 依赖模块目录 = 创建词典<文字>()
        如果 (依赖) {
            循环 (变量 k 位于 依赖) {
                变量 存在 = 具有属性(依赖, k)
                如果 (存在) {
                    变量 版本 = 依赖[k]
                    变量 键 = 正规化路径(k)
                    如果 (路径以分隔符结尾(键)) {
                        键 = 键.分切(0, -1)
                    }
                    依赖模块目录.设置(键, 正规化路径(合并路径(k, 版本)))
                }
            }
        }

        // Cache for the module resolution
        常量 解析缓存 = 创建解析缓存(编译主机,
            依赖模块目录,
            /*logChangesWhenResolvingModule*/ 为假
        );

        // Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names
        编译主机.解析模块名称组 = 主机.解析模块名称组 ?
            ((模块名称组, 包含文件, 重复使用名组) => 主机.解析模块名称组!(模块名称组, 包含文件, 重复使用名组)) :
            ((模块名称组, 包含文件, 重复使用名组) => 解析缓存.解析模块名称组(模块名称组, 包含文件, 重复使用名组));

        常量 用户提供的方案 = !!主机.解析模块名称组 ;

        报告监控诊断(诊断信息_.正在监控模式下开始编译);
        同步程序();

        // Update the wild card directory watch
        监控配置文件包含通配符目录();

        返回 配置文件名称 ?
            { 获取当前程序: 获取当前构建程序, 获取程序: 同步程序 } :
            { 获取当前程序: 获取当前构建程序, 获取程序: 同步程序, 更新根文件名称 };

        函数 获取当前构建程序() {
            返回 构建程序;
        }

        函数 获取当前程序() {
            返回 构建程序 && 构建程序.获取程序();
        }

        函数 同步程序() {
            写日志(`同步程序`);

            常量 程序 = 获取当前程序();
            如果 (具有改变编译选项) {
                新行 = 更新新行();
                如果 (程序 && 更改影响模块解析(程序.获取编译选项(), 选项组!)) {
                    解析缓存.清除();
                }
            }

            // All resolutions are invalid if user provided resolutions
            常量 具有失效解析 = 解析缓存.创建具有实现解析(用户提供的方案);
            如果 (是程序最新数据(获取当前程序(), 根文件组!, 选项组!, 获取源版本, 文件存在, 具有失效解析)) {
                返回 构建程序;
            }

            // Compile the program
            如果 (监控日志级别 !== 监控日志级别_.无_) {
                写日志('创建程序包括::');
                写日志(`  根组: ${JSON.序列化(根文件组)}`);
                写日志(`  选项组: ${JSON.序列化(选项组)}`);
            }

            具有改变编译选项 = 为假;
            解析缓存.启动每个目录的缓存解析();
            编译主机.具有失效解析 = 具有失效解析;
            编译主机.具有更改诊断类型指令名称 = 具有改变自动类型指令名称;
            构建程序 = 创建程序(根文件组!, 选项组!, 编译主机, 构建程序);
            解析缓存.完成每个目录的缓存解析();

            // Update watches
            更新失踪文件路径监控(构建程序.获取程序(), 失踪文件词典 || (失踪文件词典 = 创建词典()), 监控失踪文件路径);

            如果 (失踪请求文件路径解析) {
                // These are the paths that program creater told us as not in use any more but were 失踪的 on the disk.
                // We didnt remove the entry for them from sourceFiles cache so that we dont have to do File IO,
                // if there is already watcher for it (for 失踪的 files)
                // At this point our watches were updated, hence now we know that these paths are not tracked and need to be removed
                // so that at later time we have correct 结果 of their presence
                循环 (常量 失踪路径 属于 失踪请求文件路径解析) {
                    如果 (!失踪文件词典.具有(失踪路径)) {
                        源文件缓存.删除(失踪路径);
                    }
                }
                失踪请求文件路径解析 = 未定!;
            }

            如果 (主机.程序创建之后) {
                主机.程序创建之后(构建程序);
            }
            报告监控诊断(诊断信息_.编译完成_监视文件更改);
            返回 构建程序;
        }

        函数 更新根文件名称(文件组: 文字[]) {
            调试_.断言(!配置文件名称, '不能更新根文件名称包括配置文件监控模式');
            根文件组 = 文件组;
            计划程序更新();
        }

        函数 更新新行() {
            返回 获取新行字符(选项组!, () => 主机.获取新行());
        }

        函数 转为路径(文件名: 文字) {
            返回 ts.转为路径(文件名, 当前目录, 获取规范文件名称);
        }

        函数 是主机上的失踪文件(主机源文件: 主机文件信息_): 主机源文件 作为 主机失踪的文件_ {
            返回 类为 主机源文件 === '数字';
        }

        函数 是主机上可能的文件(主机源文件: 文件可能在主机上_): 主机源文件 作为 主机存在的文件_ {
            返回 !!(主机源文件 转为 主机存在的文件_).源文件;
        }

        函数 文件存在(文件名: 文字) {
            常量 路径 = 转为路径(文件名);
            // If 文件 is 失踪的 on host from cache, we can definitely say 文件 doesnt exist
            // otherwise we need to ensure from the disk
            如果 (是主机上的失踪文件(源文件缓存.获取(路径)!)) {
                返回 为真;
            }

            返回 目录结构主机.文件存在(文件名);
        }

        函数 获取按路径版本控制的源文件(文件名: 文字, 路径: 路径_, 正在错误?: (消息: 文字) => 无值, 应该创建新源文件?: 真假): 源文件_ {
            常量 主机源文件 = 源文件缓存.获取(路径);
            // No source 文件 on the host
            如果 (是主机上的失踪文件(主机源文件!)) {
                返回 未定!;
            }

            // Create new source 文件 if requested or the versions dont match
            如果 (!主机源文件 || 应该创建新源文件 || !是主机上可能的文件(主机源文件) || 主机源文件.版本.转为文字() !== 主机源文件.源文件.版本) {
                常量 源文件 = 获取新源文件();
                如果 (主机源文件) {
                    如果 (应该创建新源文件) {
                        主机源文件.版本++;
                    }

                    如果 (源文件) {
                        // Set the source 文件 and create 文件 watcher now that 文件 was present on the disk
                        (主机源文件 转为 主机存在的文件_).源文件 = 源文件;
                        源文件.版本 = 主机源文件.版本.转为文字();
                        如果 (!(主机源文件 转为 主机存在的文件_).文件监控者) {
                            (主机源文件 转为 主机存在的文件_).文件监控者 = 监控文件路径(主机, 文件名, 正在源文件拜拜, 轮询间隔_.Low, 路径);
                        }
                    }
                    否则 {
                        // There is no source 文件 on host any more, close the watch, 失踪的 文件 paths will track it
                        如果 (是主机上可能的文件(主机源文件)) {
                            主机源文件.文件监控者.close();
                        }
                        源文件缓存.设置(路径, 主机源文件.版本);
                    }
                }
                否则 {
                    如果 (源文件) {
                        源文件.版本 = 初始化版本.转为文字();
                        常量 文件监控者 = 监控文件路径(主机, 文件名, 正在源文件拜拜, 轮询间隔_.Low, 路径);
                        源文件缓存.设置(路径, { 源文件, 版本: 初始化版本, 文件监控者: 文件监控者 });
                    }
                    否则 {
                        源文件缓存.设置(路径, 初始化版本);
                    }
                }
                返回 源文件!;
            }
            返回 主机源文件.源文件;

            函数 获取新源文件() {
                变量 文本!: 文字;
                尝试 {
                    性能.标记('IO读之前');
                    文本 = 主机.读文件(文件名)!;
                    性能.标记('IO读之后');
                    性能.度量('I/O 读', 'IO读之前', 'IO读之后');
                }
                捕获 (e) {
                    如果 (正在错误) {
                        正在错误(e.消息);
                    }
                }

                返回 文本 !== 未定 ? 创建源文件(文件名, 文本, 主机.选项组) : 未定;
            }
        }

        函数 下个源文件版本(路径: 路径_) {
            常量 主机源文件 = 源文件缓存.获取(路径);
            如果 (主机源文件 !== 未定) {
                如果 (是主机上的失踪文件(主机源文件)) {
                    // The next version, lets set it as presence unknown 文件
                    源文件缓存.设置(路径, { 版本: 数字_(主机源文件) + 1 });
                }
                否则 {
                    主机源文件.版本++;
                }
            }
        }

        函数 获取源版本(路径: 路径_): 文字 | 未定 {
            常量 主机源文件 = 源文件缓存.获取(路径);
            返回 !主机源文件 || 是主机上的失踪文件(主机源文件) ? 未定 : 主机源文件.版本.转为文字();
        }

        函数 正在发布旧源文件(旧源文件: 源文件_, _旧选项: 编译选项_) {
            常量 主机源文件信息 = 源文件缓存.获取(旧源文件.路径);
            // If this is the source 文件 thats in the cache and new program doesnt need it,
            // remove the cached entry.
            // Note we arent deleting entry if 文件 became 失踪的 in new program or
            // there was version update and new source 文件 was created.
            如果 (主机源文件信息) {
                // record the 失踪的 文件 paths so they can be removed later if watchers arent tracking them
                如果 (是主机上的失踪文件(主机源文件信息)) {
                    (失踪请求文件路径解析 || (失踪请求文件路径解析 = [])).压入(旧源文件.路径);
                }
                否则 如果 ((主机源文件信息 转为 主机存在的文件_).源文件 === 旧源文件) {
                    如果 ((主机源文件信息 转为 主机存在的文件_).文件监控者) {
                        (主机源文件信息 转为 主机存在的文件_).文件监控者.close();
                    }
                    源文件缓存.删除(旧源文件.路径);
                    解析缓存.删除文件的解析(旧源文件.路径);
                }
            }
        }

        函数 报告监控诊断(消息: 诊断信息_) {
            如果 (主机.正在监控状态改变) {
                主机.正在监控状态改变(创建编译器诊断(消息), 新行, 选项组!);
            }
        }

        // Upon detecting a 文件 change, wait for 250ms and then perform a recompilation. This gives batch
        // operations (such as saving all modified files in an editor) a chance to complete before we kick
        // off a new compilation.
        函数 计划程序更新() {
            如果 (!主机.设置超时 || !主机.清除超时) {
                返回;
            }

            如果 (更新程序定时器) {
                主机.清除超时(更新程序定时器);
            }
            更新程序定时器 = 主机.设置超时(更新程序, 250);
        }

        函数 计划程序重新加载() {
            调试_.断言(!!配置文件名称);
            重加载级别 = 配置文件程序加载级别_.全部_;
            计划程序更新();
        }

        函数 更新程序() {
            更新程序定时器 = 未定;
            报告监控诊断(诊断信息_.检测到文件更改_正在启动增量编译);

            假如 (重加载级别) {
                若是 配置文件程序加载级别_.部分的_:
                若是 配置文件程序加载级别_.全部_:
                    返回 重加载配置文件();
                默认:
                    同步程序();
                    返回;
            }
        }

        函数 重加载配置文件() {
            写日志(`重新加载配置文件: ${配置文件名称}`);
            重加载级别 = 配置文件程序加载级别_.无_;

            如果 (缓存目录结构主机) {
                缓存目录结构主机.清除缓存();
            }
            解析配置文件();
            具有改变编译选项 = 为真;
            同步程序();
            // Update the wild card directory watch
            监控配置文件包含通配符目录();
        }

        函数 解析配置文件() {
            常量 配置解析结果 = 获取配置文件的解析命令行(配置文件名称, 解析配置文件主机);
            // 这个要通过查询源码文件夹来实现
            根文件组 = 配置解析结果!.文件名组!;
            选项组 = 配置解析结果!.选项组!;
        }

        函数 正在源文件拜拜(文件名: 文字, 事件种类: 文件监控事件种类_, 路径: 路径_) {
            更新缓存系统包括文件(文件名, 路径, 事件种类);

            // Update the source 文件 cache
            如果 (事件种类 === 文件监控事件种类_.删除的_ && 源文件缓存.获取(路径)) {
                解析缓存.使文件解析失效(路径);
            }
            下个源文件版本(路径);

            // Update the program
            计划程序更新();
        }

        函数 更新缓存系统包括文件(文件名: 文字, 路径: 路径_, 事件种类: 文件监控事件种类_) {
            如果 (缓存目录结构主机) {
                缓存目录结构主机.添加或删除文件(文件名, 路径, 事件种类);
            }
        }

        函数 监控失踪文件路径(失踪文件路径: 路径_) {
            返回 监控文件路径(主机, 失踪文件路径, 正在失踪文件改变, 轮询间隔_.Medium, 失踪文件路径);
        }

        函数 正在失踪文件改变(文件名: 文字, 事件种类: 文件监控事件种类_, 失踪路径: 路径_) {
            更新缓存系统包括文件(文件名, 失踪路径, 事件种类);

            如果 (事件种类 === 文件监控事件种类_.创建的_ && 失踪文件词典.具有(失踪路径)) {
                失踪文件词典.获取(失踪路径)!.close();
                失踪文件词典.删除(失踪路径);

                // Delete the entry in the source files cache so that new source 文件 is created
                下个源文件版本(失踪路径);

                // When a 失踪的 文件 is created, we should update the graph.
                计划程序更新();
            }
        }

        函数 监控配置文件包含通配符目录() {
            如果 (监控通配符目录) {
                清除词典(监控通配符目录, 关闭文件监控者的);
            }
        }

        函数 确保目录存在(目录路径: 文字) {
            如果 (目录路径.长度 > 获取根长度(目录路径) && !主机.目录存在!(目录路径)) {
                常量 父路径 = 获取目录路径(目录路径);
                确保目录存在(父路径);
                主机.创建目录!(目录路径);
            }
        }

        函数 写文件(文件名: 文字, 文本: 文字, 写入字节序标记: 真假, 正在错误: (消息: 文字) => 无值) {
            尝试 {
                性能.标记('IO写之前');
                确保目录存在(获取目录路径(正规化路径(文件名)));

                主机.写文件!(文件名, 文本, 写入字节序标记);

                性能.标记('IO写之后');
                性能.度量('I/O 写', 'IO写之前', 'IO写之后');
            }
            捕获 (e) {
                如果 (正在错误) {
                    正在错误(e.消息);
                }
            }
        }
    }
}
